#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;
const int basesize=1024;
int lastcode=0;
const int argvnum=64;
char* argv[argvnum];
int argc=0;

extern char** environ;
const int envnum=64;
char*env[envnum];//环境变量表
//本质是从真正的shell获取环境变量
void InitEnv(){
    int index=0;
    while(environ[index]) {
    env[index]=(char*)malloc(strlen(environ[index])+1);
    strcpy(env[index],environ[index]);
    index++;
    // env[index]=environ[index++];
    }
    env[index]=nullptr;
}


string GetUserName(){
 string name= getenv("USER");
 return name.empty()?"NULL":name;
}

string GetHostName(){
 string name= getenv("HOSTNAME");
 return name.empty()?"NULL":name;
}
char pwd[basesize];//当前工作路径
char pwdenv[basesize];//PWD的环境变量
string GetPwd(){
// string name= getenv("PWD");
snprintf(pwdenv,sizeof pwdenv,"%s%s","PWD=",pwd);
putenv(pwdenv);
   if(getcwd(pwd,sizeof pwd)==nullptr)
       return "NULL";
   return pwd;
//ZZ return name.empty()?"NULL":name;
}

string LastDir(){
    string curr=GetPwd();
    if(curr=="/"||curr=="NULL")return curr;
 string lastdir=curr.rfind("/")+curr.c_str();
 return lastdir;
}
string MakeCommandLine(){
char CommandLine[basesize];
snprintf(CommandLine,basesize,"[%s@%s %s]#",GetUserName().c_str(),GetHostName().c_str(),LastDir().c_str());
return CommandLine;
}

void PrintCommradLine(){
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);
}

bool  GetCommandLine(char*command_buffer,int size)//获取命令
{
char *result=fgets(command_buffer,size,stdin);
if(!result)
    return 0;
command_buffer[strlen(command_buffer)-1]='\0';
if(strlen(command_buffer)==0)return 0;
return 1;
}
void ParseCommandLine(char command_buffer[],int len){
  memset(argv,0,sizeof argv);
  argc=0;
  (void)(len);
    const char*sep=" ";
    argv[argc++]=strtok(command_buffer,sep);
   char*Next=NULL;
    while((bool)(Next=strtok(NULL,sep)))//不加强转会有waring
    {
        argv[argc++]=Next;
    }
}

bool ExecuteCommandLine(){
    pid_t id=fork();
    if(!id){
    execvpe(argv[0],argv,env);
        exit(3);
    }
    int status=0;
   pid_t rid= waitpid(id,&status,0);
    if(rid>0)
    {
    if(WIFEXITED(status))//跑完了，但退出码可能是非零
    {
lastcode=WEXITSTATUS(status);
    }
    else//进程异常，被终止，没跑完
    {
        lastcode=10;
    }
    return true;
    }
    return false;
}
void AddEnv(char *item){
    int index=0;
    while(env[index]){
            index++;
    }
    env[index]=(char*)malloc(strlen(item)+1);
    strcpy(env[index],item);
    env[++index]=nullptr;
}
bool CheckAndExecBuiltCommand(){
    if(0==strcmp(argv[0],"cd")){
        if(argc==2){
            chdir(argv[1]);
        }
        else
            lastcode=1;
        return true;
    }

   else  if(0==strcmp(argv[0],"export")){
        if(argc==2){
            AddEnv(argv[1]);
        }
        else
            lastcode=2;
        return true;
    }

   else  if(0==strcmp(argv[0],"env")){
       { for(int i=0;env[i];i++)
       printf("%s\n",env[i]);
   }
   lastcode=0;
   return true;
   }
   else if(0==strcmp(argv[0],"echo"))
   {
       if  (argc==2){
           if(argv[1][0]=='$'){
               if(argv[1][1]=='?')
               {
                   printf("%d\n",lastcode);
                   lastcode=0;
               }
               else{
                   const char*arr=getenv(argv[1]+1);
                  if(arr)
                   printf("%s\n",arr);
                  else
                      printf("\n");
           }
           }
           else
           {
               printf("%s\n",argv[1]);
           }
       }
       else
           lastcode=4;
       return true;
   }
    return false;
}
int main(){
   char command_buffer[basesize];
   InitEnv();
   while(true)
    { PrintCommradLine();//打印命令行提示符
   if(! GetCommandLine(command_buffer,basesize))//获取命令
   {
      continue; 
   }
    ParseCommandLine(command_buffer,(int)strlen(command_buffer));//分析命令
if( CheckAndExecBuiltCommand())//检测并执行内建命令
{
    continue;
}
    ExecuteCommandLine();//执行命令
    }
    return 0;
}
